package domain;

import java.io.Closeable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import sun.misc.Cleaner;
import sun.nio.ch.DirectBuffer;

/**
 * Result of applying LargeDoubleMatrix van http://java.dzone.com/articles/using-memory-mapped-file-huge
 * into a comparable class for a GeoTOP file to be accessed as a 4D array: 3 dimensions for the location 
 * and one for the attributes: lithostrategraphy, lithoclass and the chances of occurrence of the 
 * 7 lithoclasses distinguished now. This version will suffice till about 1 GigaByte files. Beyond that
 * look up the above website or Revision 14 and before.
 * @author kooijmanj1
 */
public class Large4DArray implements Closeable{
	private final RandomAccessFile raf;
	private final int lineSize;
	private final int pageSize;
	private final int bookSize;
	private final List<MappedByteBuffer> mappings = new ArrayList<MappedByteBuffer>();
	
	/** 
	 * @param filename Random Access File
	 * @param lineSize nr of values in lines: corresponding to nr of attribute values for one Z value or depth interval on one XY location in csv file
	 * @param pageSize nr of lines on pages:  corresponding to nr of Z values or depth intervals on one XY location in csv file
	 * @param bookSize nr of pages in books:  corresponding to nr of Y values or south to north intervals in csv file. 
	 * @param shelfSize nr of books on shelfs corresponding to nr of X values or west to east intervals in csv file.
	 */
	public Large4DArray(String filename, int lineSize, int pageSize, int bookSize, int shelfSize) throws IOException{
		this.raf = new RandomAccessFile(filename, "rw");
		this.lineSize = lineSize;
		this.pageSize = pageSize;
		this.bookSize = bookSize;
		try{
			long size = 4L * lineSize * pageSize * bookSize * shelfSize;
			mappings.add(raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size)); 
		}
		catch (IOException e){
			JOptionPane.showMessageDialog(null, "Error configuring raf file");
			raf.close();
		}
	}
	
	protected long position(int words, int lines, int pages, int books ){
		return (long) words + lines * lineSize + pages * pageSize * lineSize + 
				books * bookSize * pageSize * lineSize;
	}
	
	public float get(int words, int lines, int pages, int books){
		long p = position(words, lines, pages, books) * 4;
		return ((ByteBuffer)mappings.get(0)).getFloat((int)p);
	}
	
	public void set(int words, int lines, int pages, int books, float f){
		long p = position(words, lines, pages, books) * 4;
		((ByteBuffer)mappings.get(0)).putFloat((int)p, f);
	}
	
	public void close() throws IOException{
		for (MappedByteBuffer mapping : mappings){
			clean(mapping);
			raf.close();
		}
	}
	
	private void clean(MappedByteBuffer mapping){
		if (mapping == null) return;
		Cleaner cleaner = ((DirectBuffer)mapping).cleaner();
		if (cleaner != null) cleaner.clean();
	}
}
